import guicomponents.GButton;
import guicomponents.GCScheme;
import guicomponents.GComponent;
import guicomponents.GConstants;
import guicomponents.GLabel;
import guicomponents.GOption;
import guicomponents.GOptionGroup;
import guicomponents.GTextField;
import jade.core.Actor;
import jade.util.Coord;
import jade.util.Dice;

import java.awt.Color;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Vector;
import java.util.logging.Logger;

import org.ini4j.Ini;

import processing.core.PApplet;
import processing.core.PFont;
import ddf.minim.AudioSample;
import ddf.minim.AudioSnippet;
import ddf.minim.Minim;

public class PrisonEscape extends PApplet {
	private enum State {
		MENU, LOADING_SCREEN, GAME, HELP, LOAD, SAVE, LOST, WON, OPTIONS, BACK
	}

	private Logger logger = Logger.getLogger(PrisonEscape.class.getName());

	boolean debug = false;
	boolean realTime = false;
	boolean allowGoingBackUp = false;
	private int ticksSinceLastMove;
	private boolean justStarted;
	private boolean stateChanged;
	private boolean gameOverProcessed;
	boolean updateScreen;
	private boolean gameOptions;

	private Dungeon dungeon;
	Level level;
	private Player player;

	private int fontHeight;
	private int fontWidth;
	private int paddingLines;
	private int paddingColumns;
	private int levelWidth;
	private int levelHeight;
	private int buttonWidth;

	private String playerName = "";
	private int startHp;
	private int startAtk;
	private int startDef;
	private int startDmg;

	private int l_diff;
	private int l_size;
	private String filename;
	private Ini ini;

	private GButton b_newGame;
	private GButton b_loadGame;
	private GButton b_options;

	private GButton b_back;
	private GButton b_save;
	private GButton b_startGame;
	private GButton b_help;

	private GTextField playerField;
	private GLabel l_player;

	private GOptionGroup rb_difficulty;
	private GOption o_easy;
	private GOption o_medium;
	private GOption o_hard;

	/*private GOptionGroup rb_l_size;
	private GOption s_small;
	private GOption s_medium;
	private GOption s_large;*/

	private Minim minim;
	private AudioSample menuClickSound;
	private AudioSnippet menuMusic;
	private AudioSnippet gameMusic;
	private AudioSample missSound;
	private AudioSample killSound;
	private AudioSample descSound;
	private AudioSample walkSound;

	private State currentState;

	private PFont font;
	private PFont largeFont;
	private int tileWidth = -1;

	private List<Character> mainMenuKeys;
	private List<Character> newGameKeys;
	private List<Character> optionsKeys;
	private List<Character> continueKeys;
	private List<Character> saveKeys;
	private List<Character> backKeys;
	private List<Character> startKeys;
	private List<Character> helpKeys;
	private List<Integer> shiftKeys;

	private Vector<GButton> menuButtons;
	private Vector<GButton> optionButtons;
	private Vector<GButton> goptionButtons;
	private Vector<GButton> helpButtons;

	private GButton focused;
	private int lastKey;
	private int keyCounter;
	private int clickCounter;

	@Override
	public void draw() {
		// logger.info("draw key: " + key + ", " + currentState);

		if (currentState != State.GAME) {
			/*
			 * focused = getFocussedButton(); if (focused != null){
			 * focused.setColorScheme(GCScheme.GREEN_SCHEME); }
			 */
			if (GComponent.getFocusObject() instanceof GButton)
				focusButton((GButton) GComponent.getFocusObject(), true);
			/*if (GComponent.getFocusObject() != null)
				logger.info(GComponent.getFocusObject().getText());*/

			if (shiftKeys.contains(keyCode) || key == TAB) {

				if (currentState == State.MENU) {
					if (keyCode == UP || keyCode == DOWN) {
						shiftFocus(menuButtons, focused, keyCode);
					} else if (key == TAB) {
						shiftFocus(menuButtons, focused, key);
					}

				} else if (currentState == State.OPTIONS) {

					if (keyCode == RIGHT || keyCode == LEFT) {

						if (gameOptions) {
							shiftFocus(goptionButtons, focused, keyCode);
						} else
							shiftFocus(optionButtons, focused, keyCode);
					} else if (key == TAB) {
						if (gameOptions) {
							shiftFocus(goptionButtons, focused, key);
						} else
							shiftFocus(optionButtons, focused, key);
					} 
				}//options
				else if (currentState == State.HELP) {
					logger.info("help shift");
					if (key == TAB) {
						logger.info("help shift");
						shiftFocus(helpButtons, focused, key);
					} else if (keyCode == RIGHT || keyCode == LEFT) {
						logger.info("help shift");
						shiftFocus(helpButtons, focused, keyCode);
					}

				}
			} 
		}

		updateScreen = updateScreen || realTime;
		if (currentState != State.GAME || updateScreen)
			background(0);

		if (stateChanged || justStarted)
			playMusic();

		if (currentState == State.GAME) {
			gameTick();
			if (updateScreen || player.changedLevel) {
				if (player.changedLevel) {
					background(0);
					player.clearFov();
					player.calcFoV();
					player.changedLevel = false;
				}
				drawGame();
				updateScreen = false;
			}

			if (mainMenuKeys.contains(key)) {
				setState(State.MENU);
				focusButton(b_newGame, true);
				stateChanged = true;
				updateScreen = true;
			} else
				stateChanged = false;
		} else if (currentState == State.MENU) {
			drawMenu();

			if (newGameKeys.contains(key)) {
				setState(State.OPTIONS);
				menuClickSound.trigger();
				gameOptions = true;
				background(0);
				hideMenu();
				drawOptions();
				stateChanged = true;
			} else if (optionsKeys.contains(key)) {
				menuClickSound.trigger();
				setState(State.OPTIONS);
				stateChanged = true;
			} else
				stateChanged = false;
		} else if (currentState.equals(State.LOADING_SCREEN)) {
			hideMenu();
			drawLoading();
		} else if (currentState.equals(State.LOST)) {
			drawLost();

			if (continueKeys.contains(key)) {
				setState(State.MENU);
				stateChanged = true;
			} else
				stateChanged = false;
		} else if (currentState.equals(State.WON)) {
			drawWon();

			if (continueKeys.contains(key)) {
				setState(State.MENU);
				stateChanged = true;
			} else
				stateChanged = false;
		} else if (currentState == State.OPTIONS) {
			drawOptions();

			if (!playerField.hasFocus()) {
				// Save changes
				if (saveKeys.contains(key)) {
					// TODO: save changes
					
						saveConfig();
						if(gameOptions){
							setState(State.GAME);
							hideOptions();
							newGame();
							stateChanged = true;
						}
					
				}// Back without saving
				else if (backKeys.contains(key)) {
					setState(State.MENU);
					focused = b_newGame;
					stateChanged = true;
				} else if (helpKeys.contains(key)) {
					setState(State.HELP);
					// focused = b_startGame;
					stateChanged = true;
				} else if (startKeys.contains(key)) {
					setState(State.GAME);
					hideOptions();
					newGame();
					stateChanged = true;
				} else
					stateChanged = false;
			}
		} else if (currentState.equals(State.HELP)) {
			drawHelp();

			if (startKeys.contains(key)) {
				setState(State.GAME);
				hideOptions();
				newGame();
				stateChanged = true;
			} else if (backKeys.contains(key)) {
				key = 0;
				logger.info("key Opt: " + key);
				setState(State.OPTIONS);
				gameOptions = true;
				stateChanged = true;
			}
		}
		// Only show GUI when options are displayed
		if (currentState != State.OPTIONS && playerField.isVisible())
			hideOptions();
		// Only show GUI when menu is displayed
		if (currentState != State.MENU && b_newGame.isVisible())
			hideMenu();
	}

	private void drawActor(Actor actor) {
		int tileX = (actor.getX() + paddingColumns / 2) * fontWidth
				+ (fontWidth - tileWidth) / 2;
		int tileY = (actor.getY() + paddingLines / 2 + 1) * fontHeight;

		fill(actor.getLook().getColor().getRGB());
		text(actor.getLook().getCh(), tileX, tileY);
	}

	private void drawActors() {
		drawActor(player);
		for (Enemy e : dungeon.getLevel().getEnemies())
			drawActor(e);
	}

	private void drawGame() {
		fill(255);
		String titleString = "Prison Escape";
		text(titleString, width / 2 - textWidth(titleString) / 2, fontHeight);
		text(playerName, width / 2 - textWidth(playerName) / 2, fontHeight * 2);

		// Footer user interface
		String hpString = "HP: "
				+ IntToBar(player.getHp().value(), player.getMaxHp());
		String hpValues = player.getHp() + "/" + player.getMaxHp();
		text(hpString, fontWidth, fontHeight
				* (levelHeight + paddingLines / 2 + 1));
		text(hpValues, fontWidth, fontHeight
				* (levelHeight + paddingLines / 2 + 1) + fontHeight);
		String guardString = "Guards: " + level.enemies.size();
		text(guardString, width / 2 - textWidth(guardString) / 2, fontHeight
				* (levelHeight + paddingLines / 2 + 1));
		String levelString = "Level: " + dungeon.getDepth();
		text(levelString, width - textWidth(levelString) - fontWidth,
				fontHeight * (levelHeight + paddingLines / 2 + 1));

		text(player.getMessages(), width / 2 - textWidth(player.getMessages())
				/ 2, fontHeight * (levelHeight + paddingLines / 2 + 2));

		if (player.light)
			drawLevel();
		drawActors();
	}

	private void drawLevel() {
		if (tileWidth == -1)
			tileWidth = (int) textWidth(' ');

		for (int x = 0; x < level.width; x++)
			for (int y = 0; y < level.height; y++) {
				int tileX = (x + paddingColumns / 2) * fontWidth
						+ (fontWidth - tileWidth) / 2;
				int tileY = (y + paddingLines / 2 + 1) * fontHeight;
				if (level.sight[y][x] == Sight.IN_SIGHT) {
					fill(127);
					rect((x + paddingColumns / 2) * fontWidth,
							(y + paddingLines / 2) * fontHeight + 3, fontWidth,
							fontHeight);
					fill(level.getLook(x, y).getColor().getRGB());
					text(level.getLook(x, y).getCh(), tileX, tileY);
				} else if (debug || level.sight[y][x] == Sight.SEEN || level.getLook(x, y).getCh() == '>') {
					if(level.getLook(x, y).getCh() == '>')
						fill(level.getLook(x, y).getColor().getRGB());
					else
						fill(Color.gray.getRGB());
					text(level.getLook(x, y).getCh(), tileX, tileY);
				} else if (!debug) {
					fill(0);
					rect((x + paddingColumns / 2) * fontWidth,
							(y + paddingLines / 2) * fontHeight + 3, fontWidth,
							fontHeight);
				}
			}
	}

	private void drawLoading() {
		fill(255);
		String loadingString = "Loading...";
		text(loadingString, width / 2 - textWidth(loadingString) / 2,
				height / 2);
	}

	private void drawLost() {
		if (!gameOverProcessed) {
			playSounds(Sound.LOST);
			gameOverProcessed = true;
		}

		fill(Color.red.getRGB());
		textFont(largeFont, fontHeight * 3);

		String loseMessage = "YOU LOSE! ;(";
		String pressMessage = "PRESS SPACE";

		text(loseMessage, width / 2 - textWidth(loseMessage) / 2, fontHeight
				* (levelHeight + paddingLines) / 2);
		textFont(font, fontHeight);

		text(pressMessage, width / 2 - textWidth(pressMessage) / 2, fontHeight
				* (levelHeight + paddingLines) / 2 + fontHeight);

		textFont(font, fontHeight);
	}

	private void drawMenu() {

		// Menu
		fill(255);
		String titleString = "Prison Escape";
		text(titleString, width / 2 - textWidth(titleString) / 2, fontHeight
				* ((levelHeight + paddingLines) / 2 - 4));
		String underlineString = "=============";
		text(underlineString, width / 2 - textWidth(underlineString) / 2,
				fontHeight * ((levelHeight + paddingLines) / 2 - 2));

		b_newGame.setVisible(true);
		b_loadGame.setVisible(true);
		b_options.setVisible(true);
	}

	private void drawOptions() {
		textFont(font, fontHeight);
		fontWidth = fontHeight;

		fill(255);
		String OptionsString = "Options";
		text(OptionsString, width / 2 - textWidth(OptionsString) / 2,
				fontHeight * ((levelHeight + paddingLines) / 2 - 4));

		fill(255);
		String underlineString = "=============";
		text(underlineString, width / 2 - textWidth(underlineString) / 2,
				fontHeight * ((levelHeight + paddingLines) / 2 - 2));


		fill(255);
		String difficutlyString = "Level Difficulty";
		text(difficutlyString, width / 2 - textWidth(difficutlyString) / 2,
				fontHeight * ((levelHeight + paddingLines) / 2 + 2));

		// TODO: Options
		playerField.setVisible(true);
		l_player.setVisible(true);

		o_easy.setVisible(true);
		o_medium.setVisible(true);
		o_hard.setVisible(true);

		if (gameOptions) {
			b_startGame.setVisible(true);
			b_help.setVisible(false);
			b_back.setX(width * 4
		/ 5 - buttonWidth / 2);
			b_back.setVisible(true);
			b_save.setVisible(false);
		} else {
			//b_back.setX(backX);
			b_back.setVisible(true);
			b_save.setVisible(true);
			b_startGame.setVisible(false);
			b_help.setVisible(false);
		}

		textFont(font, fontHeight);
	}

	private void drawHelp() {
		textFont(font, fontHeight);
		fontWidth = fontHeight;
		fill(255);
		String helpString = "Help";
		text(helpString, width / 2 - textWidth(helpString) / 2, fontHeight
				* ((levelHeight + paddingLines) / 2 - 4));

		fill(255);
		int backX = width * 4 / 5 - buttonWidth / 2;

		String underlineString = "=============";
		text(underlineString, width / 2 - textWidth(underlineString) / 2,
				fontHeight * ((levelHeight + paddingLines) / 2 - 2));
		
		String helpText = "Use the arrows to move and press space to switch the lights on or off.";
		text(helpText , width / 2 - textWidth(helpText ) / 2,
				fontHeight * ((levelHeight + paddingLines) / 2));
		
		b_startGame.setVisible(true);
		b_back.setX(backX);
		b_back.setVisible(true);
		textFont(font, fontHeight);
	}

	private void drawWon() {
		if (!gameOverProcessed) {
			playSounds(Sound.WON);
			gameOverProcessed = true;
		}
		fill(Color.blue.getRGB());
		textFont(largeFont, fontHeight * 3);

		String wonMessage = "YOU WIN! :D";
		String pressMessage = "PRESS SPACE";

		text(wonMessage, width / 2 - textWidth(wonMessage) / 2, fontHeight
				* (levelHeight + paddingLines) / 2);

		text(pressMessage, width / 2 - textWidth(pressMessage) / 2, fontHeight
				* (levelHeight + paddingLines) / 2 + fontHeight);

		textFont(font, fontHeight);
	}

	private void gameTick() {
		if (realTime)
			ticksSinceLastMove++;

		level = dungeon.getLevel();

		if (player.isExpired())
			currentState = State.LOST;
		else if (player.won)
			currentState = State.WON;

		if (realTime)
			if (ticksSinceLastMove == 10) {
				ticksSinceLastMove = 0;
				for (Enemy e : level.enemies)
					e.move = true;
			}

		level.tick();
	}

	private GButton getFocusedButton() {
		GComponent focused = GComponent.getFocusObject();
		if (focused instanceof GButton) {
			return (GButton) focused;
		} else
			return null;
	}

	/**
	 * fetch specified key set from ini-file
	 * 
	 * @param fetch
	 *            : name of List
	 * @param key_name
	 *            : name of option in ini-file
	 */
	void getKeys(List<Character> fetch, String key_name) {
		String[] keys = ini.get("keys", key_name).replaceAll(" ", "")
				.replaceAll("SPACE", " ").split(",");
		for (String key : keys)
			if (key.equals("ESC")) {
				fetch.add(ESC);
			} else {
				fetch.add(key.charAt(0));
			}

	}

	private void initKeys() {

		mainMenuKeys = new Vector<Character>();
		newGameKeys = new Vector<Character>();
		optionsKeys = new Vector<Character>();
		continueKeys = new Vector<Character>();
		saveKeys = new Vector<Character>();
		backKeys = new Vector<Character>();
		startKeys = new Vector<Character>();
		helpKeys = new Vector<Character>();

		getKeys(mainMenuKeys, "mainMenuKeys");
		getKeys(newGameKeys, "newGameKeys");
		getKeys(optionsKeys, "optionsKeys");
		getKeys(continueKeys, "continueKeys");
		getKeys(saveKeys, "saveKeys");
		getKeys(backKeys, "backKeys");
		getKeys(startKeys, "startKeys");
		getKeys(helpKeys, "helpKeys");

		shiftKeys = Arrays.asList(UP, DOWN, LEFT, RIGHT);

		/*
		 * mainMenuKeys = Arrays.asList('m', 'M', ESC); newGameKeys =
		 * Arrays.asList('n', 'N'); optionsKeys = Arrays.asList('o', 'O');
		 * continueKeys = Arrays.asList(' '); saveKeys = Arrays.asList('s',
		 * 'S'); backKeys = new Vector<Character>(Arrays.asList('b', 'B'));
		 * backKeys.addAll(mainMenuKeys); // Go to menu
		 */
	}

	private void initOptions() {
		// Options GUI
		// Player name
		playerField = new GTextField(this, "", 2 * paddingColumns + levelWidth
				+ buttonWidth, fontHeight
				* ((levelHeight + paddingLines) / 2 - 1), buttonWidth, 20);
		playerField.setFocus(false);
		playerField.setText(playerName);
		playerField.setVisible(false);

		l_player = new GLabel(this, "Playername:",
				(2 * paddingColumns + levelWidth), fontHeight
						* ((levelHeight + paddingLines) / 2 - 1), buttonWidth,
				20);
		l_player.setColorScheme(GCScheme.GREEN_SCHEME);
		l_player.setVisible(false);

		int optionWidth = 100;

		// difficulty
		rb_difficulty = new GOptionGroup();
		o_easy = new GOption(this, "easy", 2 * paddingColumns
				+ levelWidth + buttonWidth, fontHeight
				* ((levelHeight + paddingLines) / 2 + 2) + fontHeight, optionWidth);
		o_medium = new GOption(this, "medium",
				2 * paddingColumns
				+ levelWidth + buttonWidth, fontHeight
				* ((levelHeight + paddingLines) / 2 + 2) + fontHeight * 2, optionWidth);
		o_hard = new GOption(this, "hard", 2 * paddingColumns
				+ levelWidth + buttonWidth, fontHeight
				* ((levelHeight + paddingLines) / 2 + 2) + fontHeight * 3, optionWidth);

		rb_difficulty.addOption(0, o_easy);
		rb_difficulty.addOption(1, o_medium);
		rb_difficulty.addOption(2, o_hard);
		rb_difficulty.setSelected(l_diff);
		o_easy.setColorScheme(GCScheme.GREEN_SCHEME);
		o_medium.setColorScheme(GCScheme.GREEN_SCHEME);
		o_hard.setColorScheme(GCScheme.GREEN_SCHEME);
		o_easy.setVisible(false);
		o_medium.setVisible(false);
		o_hard.setVisible(false);

		b_back = new GButton(this, "Back [B]", State.BACK.ordinal(), width / 5
				- buttonWidth / 2, fontHeight * levelHeight, buttonWidth,
				fontHeight);
		b_back.setVisible(false);
		b_back.setText("Back [B]");

		b_save = new GButton(this, "Save [S]", State.SAVE.ordinal(), width * 4
				/ 5 - buttonWidth / 2, fontHeight * levelHeight, buttonWidth,
				fontHeight);
		b_save.setVisible(false);
		b_save.setText("Save [S]");

		b_startGame = new GButton(this, "Start Game [S]", State.GAME.ordinal(),
				width / 5 - buttonWidth / 2, fontHeight * levelHeight,
				buttonWidth, fontHeight);
		b_startGame.setVisible(false);
		b_startGame.setText("Start Game [S]");

		b_help = new GButton(this, "Help [H]", State.HELP.ordinal(), width * 4
				/ 5 - buttonWidth / 2, fontHeight * levelHeight, buttonWidth,
				fontHeight);
		b_help.setVisible(false);
		b_help.setText("Help [H]");

		optionButtons = new Vector<GButton>();
		goptionButtons = new Vector<GButton>();
		helpButtons = new Vector<GButton>();

		optionButtons.add(b_back);
		optionButtons.add(b_save);

		goptionButtons.add(b_startGame);
		goptionButtons.add(b_back);

		helpButtons.add(b_startGame);
		helpButtons.add(b_back);

		textFont(font, fontHeight);
	}

	private void initMenu() {
		
		logger.info("init menu");
		
		// Menu GUI
		b_newGame = new GButton(this, "New Game [N]", State.GAME.ordinal(),
				width / 2 - buttonWidth / 2, fontHeight
						* ((levelHeight + paddingLines) / 2 - 1), buttonWidth,
				fontHeight);
		b_newGame.setVisible(false);
		// b_newGame.setColorScheme(1);
		b_newGame.setText("New Game [N]");

		b_loadGame = new GButton(this, "Load Game [L]", State.LOAD.ordinal(),
				width / 2 - buttonWidth / 2, fontHeight
						* ((levelHeight + paddingLines) / 2), buttonWidth,
				fontHeight);
		b_loadGame.setVisible(false);
		b_loadGame.setText("Load Game [L]");

		b_options = new GButton(this, "Options [O]", State.OPTIONS.ordinal(),
				width / 2 - buttonWidth / 2, fontHeight
						* ((levelHeight + paddingLines) / 2 + 1), buttonWidth,
				fontHeight);
		b_options.setVisible(false);
		b_options.setText("Options [O]");

		focusButton(b_newGame, true);
		GComponent focusedComp = GComponent.getFocusObject();
		if (focusedComp != null)
			logger.info("focused name: " + focusedComp.getText());
		if (focusedComp instanceof GButton)
			focused = (GButton) focusedComp;
		
		menuButtons = new Vector<GButton>();
		menuButtons.add(b_newGame);
		menuButtons.add(b_loadGame);
		menuButtons.add(b_options);

		textFont(font, fontHeight);

	}

	/**
	 * Reads configuration data from ini-file
	 */
	private void getConfig() throws IOException {

		fontHeight = ini.get("config", "fontHeight", int.class);
		buttonWidth = ini.get("config", "buttonWidth", int.class);
		debug = ini.get("config", "debug", boolean.class);
		realTime = ini.get("config", "realTime", boolean.class);
		allowGoingBackUp = ini.get("config", "allowGoingBackUp", boolean.class);

		paddingLines = ini.get("level", "paddingLines", int.class);
		paddingColumns = ini.get("level", "paddingColumns", int.class);
		levelWidth = ini.get("level", "levelWidth", int.class);
		levelHeight = ini.get("level", "levelHeight", int.class);
		l_size = ini.get("level", "levelSize", int.class);

		playerName = ini.get("stats", "playerName");
		startHp = ini.get("stats", "startHp", int.class);
		startAtk = ini.get("stats", "startAtk", int.class);
		startDmg = ini.get("stats", "startDmg", int.class);
		startDef = ini.get("stats", "startDef", int.class);
		l_diff = ini.get("stats", "difficulty", int.class);
		
		setConfig();
	}

	/**
	 * method to handle Button events
	 * 
	 * @param button
	 */
	public void handleButtonEvents(GButton button) {
		key = 0;
		stateChanged = true;
		boolean click = false;
		int maxCount = 0;
		
		if (button == focused){
			maxCount = 0;
		}
		else maxCount = 1;
		
		
			clickCounter++;
			logger.info("click counter: " + clickCounter);
			if(clickCounter > maxCount){
				click = true;
				clickCounter = 0;
			}

		if (click) {
			if (button != null) {
				
				logger.info(button.getText());

				if (focused != null) {
					logger.info(focused.getText());
					focused.setFocus(false);
					focused.setColorScheme(GCScheme.BLUE_SCHEME);
				}

				logger.info("clicked");
				menuClickSound.trigger();
				if (button == b_newGame) {
					setState(State.OPTIONS);
					gameOptions = true;
					focused = b_startGame;
					background(0);
					hideMenu();
					// drawOptions();
				} else if (button == b_loadGame) {
					setState(State.LOAD);
				} else if (button == b_options) {
					setState(State.OPTIONS);
					if (gameOptions) {
						// focused = b_startGame;
					} // else focused = b_back;
					// drawOptions();
				} else if (button == b_back) {
					if (currentState.equals(State.HELP)) {
						gameOptions = true;
						// focusButton(b_startGame, true);
						setState(State.OPTIONS);
					} else {
						focusButton(b_newGame, true);
						setState(State.MENU);
					}

				} else if (button == b_save) {
					/*
					 * try { saveConfig(filename); } catch (IOException e) {
					 * logger.warning(e.getMessage()); e.printStackTrace(); }
					 */
					focusButton(b_newGame, true);
					setState(State.MENU);
					// drawMenu();
				} else if (button == b_startGame) {
					if (currentState.equals(State.OPTIONS)) {
						saveConfig();
						setState(State.HELP);
					} else if (currentState.equals(State.HELP)) {
						setState(State.LOADING_SCREEN);
						hideOptions();
						drawLoading();
						newGame();
						// FIXME: find out why the gui lib changes the font and remove
						// the following line
						textFont(font, fontHeight);
						drawGame();
					}

				} else if (button == b_help) {
					focusButton(b_startGame, true);
					setState(State.HELP);
					hideOptions();
					// drawHelp();
				}
			}//button
		}

	}

	/**
	 * method to handle Option events
	 * 
	 * @param selected
	 */
	public void handleOptionEvents(GOption selected, GOption deselected) {

		logger.info("selected");
		if (focused != null)
			focusButton(focused, false);

		if (selected == o_easy && selected.eventType == GConstants.CLICKED) {
			menuClickSound.trigger();
			rb_difficulty.setSelected(selected);
		} else if (selected == o_medium
				&& selected.eventType == GConstants.CLICKED) {
			menuClickSound.trigger();
			rb_difficulty.setSelected(selected);
		} else if (selected == o_hard
				&& selected.eventType == GConstants.CLICKED) {
			menuClickSound.trigger();
			rb_difficulty.setSelected(selected);
		}
	}

	/**
	 * method to handle Textfield events
	 * 
	 * @param textfield
	 */
	public void handleTextFieldEvents(GTextField textfield) {
		if (textfield.eventType == GConstants.ENTERED
				|| textfield.eventType == GConstants.CLICKED) {
			logger.info("tf changed");
			textfield.setFocus(true);
			logger.info(focused.getText());
			focusButton(focused, false);
		}

	}

	private void hideMenu() {
		b_newGame.setVisible(false);
		b_loadGame.setVisible(false);
		b_options.setVisible(false);
	}

	private void hideOptions() {
		gameOptions = false;
		playerField.setVisible(false);
		o_easy.setVisible(false);
		o_medium.setVisible(false);
		o_hard.setVisible(false);
		b_back.setVisible(false);
		b_save.setVisible(false);
		b_startGame.setVisible(false);
		b_help.setVisible(false);
		l_player.setVisible(false);
	}

	private String IntToBar(int i, int maxI) {
		int points = 5;
		float NormI = i * (float) points / maxI;

		String bar = "[";
		for (int j = 0; j < points; j++)
			if (j < NormI)
				bar += "=";
			else
				bar += ".";

		return bar + "]";
	}

	@Override
	public void keyPressed() {
		// press button
		if (!currentState.equals(State.GAME)) {
			if ((key == ENTER || key == RETURN) && !playerField.hasFocus()) {
				//focused = getFocusedButton();
				if (focused != null) {
				}
					handleButtonEvents(focused);
			}
			if (key == TAB) {
				if (currentState == State.OPTIONS) {

					if (getFocusedButton() != null) {
						focusButton(getFocusedButton(), false);
					}
					if (gameOptions) {
						focusButton(b_startGame, true);
					} else {
						focusButton(b_save, true);
					}
				} else if (currentState == State.HELP) {

					if (getFocusedButton() != null) {
						focusButton(getFocusedButton(), false);
					}
					focusButton(b_startGame, true);
				}
			}
		}
		if (key == ESC)
			// To prevent processing from closing the app
			key = mainMenuKeys.get(0);

		if (currentState.equals(State.GAME))
			player.move = true;

		updateScreen = true;
	}

	private void newGame() {
		if (missSound == null) {
			missSound = minim.loadSample("wrong1.wav");
			killSound = minim.loadSample("dead4.wav");
			descSound = minim.loadSample("stairs.wav");
			walkSound = minim.loadSample("footstep2.wav");
		}

		gameOverProcessed = false;
		ticksSinceLastMove = 0;

		dungeon = new Dungeon(this, new Dice(millis()), levelWidth, levelHeight);
		level = dungeon.getLevel();
		player = new Player(this, dungeon, playerName, startHp, startAtk,
				startDef, startDmg);
		Coord startPos = level.getOpenTile();
		level.addActor(player, startPos.getX(), startPos.getY());
		player.setPos(startPos.getX(), startPos.getY());

		currentState = State.GAME;
		updateScreen = true;
	}

	private void playMusic() {
		if (currentState.equals(State.LOADING_SCREEN))
			drawLoading();

		stateChanged = false;
		justStarted = false;
		if (currentState.equals(State.GAME)) {
			if (gameMusic == null)
				gameMusic = minim
						.loadSnippet("ftm_ambient_creepy bell tower.mp3");

			if (!gameMusic.isPlaying()) {
				menuMusic.pause();
				gameMusic.loop();
			}

		} else if (currentState.equals(State.MENU)
				|| currentState.equals(State.OPTIONS)
				|| currentState.equals(State.LOAD)
				|| currentState.equals(State.LOADING_SCREEN)) {
			if (menuMusic == null)
				menuMusic = minim.loadSnippet("ftm_calm retaliation.mp3");

			if (!menuMusic.isPlaying()) {
				if (gameMusic != null && gameMusic.isPlaying())
					gameMusic.pause();
				menuMusic.loop();
			}

			if (menuClickSound == null) {
				// Load click sounds in advance to reduce playback delay
				menuClickSound = minim.loadSample("click.wav");
				// Done with initial loading phase
				currentState = State.MENU;
			}
		}
	}

	/**
	 * method for playing/triggering music/sound effects
	 * 
	 * @param sound
	 */
	void playSounds(Sound sound) {
		switch (sound) {
		case WALK:
			walkSound.trigger();
			break;
		case MISS:
			missSound.trigger();
			break;
		case KILL:
			killSound.trigger();
			break;
		case DESC:
			descSound.trigger();
			break;
		case WON:
			// FIXME: in play music
			gameMusic.pause();
			break;
		case LOST:
			// FIXME: in play music
			gameMusic.pause();
			break;
		}
	}

	private void saveConfig(){ 
		// FIXME: Save to cookie or server instead

		// Get data
		playerName = playerField.getText();
		l_diff = rb_difficulty.selectedIndex();

		logger.info("levelDiff: " + l_diff);
		
		setConfig();

	}
	
	private void setConfig(){
		
		logger.info("l_diff: " + l_diff);
		logger.info("l_size: " + l_size);
		
		switch(l_diff){
		case 0: //easy
			startHp = 10;
			break;
		case 1: //medium
			startHp = 5;
			break;
		case 2: //hard
			startHp = 3;
			break;
		}
		
	}

	private void focusButton(GButton button, boolean focus) {
		if (button != null) {
			if (focus) {
				button.setFocus(true);
				button.setColorScheme(GCScheme.GREEN_SCHEME);
				focused = button;
			} else if (!focus) {
				button.setFocus(false);
				button.setColorScheme(GCScheme.BLUE_SCHEME);
				focused = null;
			}
		}
	}

	void setState(State state) {
		if (state.equals(State.MENU)) {
			focusButton(b_newGame, true);
		} /*
		 * else if (state.equals(State.OPTIONS)) { if (gameOptions) { focused =
		 * b_startGame; } else focused = b_back; } else if
		 * (state.equals(State.HELP)) { focused = b_startGame; }
		 */
		currentState = state;
		logger.info("set state: " + state);
		// FIXME: key change
		key = 0;
		logger.info("key: " + key);
	}

	private void shiftFocus(Vector<GButton> buttons, GButton fButton,
			int pressedKey) {
		
		int i = 0;
		int maxCount = 0; //max for keyCounter
		boolean shift = true;
		GButton newButton = null;
		
		if (pressedKey == TAB && !currentState.equals(State.MENU)){
			maxCount = 3;
		} else maxCount = 1;
		
		//delay reaction on key press
			if(pressedKey != lastKey){
				shift = true;
				keyCounter = 0;
			}
			else if (pressedKey == lastKey){
				keyCounter++;
				logger.info("shift counter:" + keyCounter);
				if (keyCounter > maxCount){
					shift = true;
					keyCounter = 0;
				}
				else shift = false;
			}
			else shift = false;
		
		if (shift) {
			for (GButton b : buttons) {
				if (b == fButton) {
					if (pressedKey == DOWN || pressedKey == RIGHT || pressedKey == TAB) {
						if (i < buttons.size() - 1) {
							newButton = buttons.get(i + 1);
						} else if (i == buttons.size() - 1) {
							newButton = buttons.get(0);
						}
					} else if (pressedKey == UP || pressedKey == LEFT) {

						buttons.get(i).setFocus(false);
						if (i == 0) {
							newButton = buttons.get(buttons.size() - 1);
						} else if (i > 0 && i < buttons.size()) {
							newButton = buttons.get(i - 1);
						}
					}
					continue;
				}
				i++;
			}
			if (newButton != null) {
				focusButton(fButton, false);
				focusButton(newButton, true);
				focused = newButton;
			}
		}
		lastKey = pressedKey;
		keyCode = 0;
		key = 0;
	}

	@Override
	public void setup() {
		frameRate(15);

		filename = "data/config.ini";
		logger.info("setup");

		justStarted = true;
		stateChanged = false;
		// Get config data
		try {
			ini = new Ini(getClass().getResourceAsStream(filename));
			getConfig();
		} catch (IOException e) {
			logger.warning(e.getMessage());
			e.printStackTrace();
		}

		initKeys();

		minim = new Minim(this);
		currentState = State.LOADING_SCREEN;

		font = loadFont("Consolas-" + fontHeight + ".vlw");
		largeFont = loadFont("Consolas-" + fontHeight*3 + ".vlw");
		textAlign(LEFT);
		textFont(font, fontHeight);
		fontWidth = fontHeight;

		initMenu();

		initOptions();

		size((levelWidth + paddingColumns) * fontWidth,
				(levelHeight + paddingLines) * fontHeight);

		logger.info("size: " + width + "x" + height);

		smooth();
		noStroke();
	}

}
